/***************************
 * Description:
 *      A developer console similar to Valve's current version.
 *      The idea was to port their professional tool for XNA users/developers to utilize.
 * Author: Niels-David Yogi
 * GFX produced by Palve Bodi. 
 ***************************/

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DeveloperConsole
{
    public sealed class DeveloperConsole
    {
        private enum DevLineState { NONE, DEVELOPER, DEVELOPER2 };
        private enum FocusState { HISTORY, INPUT };
        private struct LineContainer
        {
            public string sValue;
            public Color color;
            public int iLine;
            public DevLineState state;
            public LineContainer(string value, Color color)
            {
                sValue = value;
                iLine = -1;
                state = DevLineState.NONE;
                this.color = color;
            }
            public LineContainer(string value, Color color, DevLineState state)
            {
                sValue = value;
                iLine = -1;
                this.color = color;
                this.state = state;
            }
        }
        private struct DisplayFields
        {
            public Rectangle defaultValue;
            public Rectangle value;
            public Texture2D texture;
            public string sTextureLocation;
            public DisplayFields(int x, int y, int width, int height, string sTexture)
            {
                defaultValue = new Rectangle(x, y, width, height);
                value = new Rectangle(x, y, width, height);
                sTextureLocation = sTexture;
                texture = null;
            }
            public void SetWidth(int width)
            {
                value.Width = width;
            }
            public void SetHeight(int height)
            {
                value.Height = height;
            }
            public void LoadTexture(ContentManager contentManager)
            {
                if (contentManager != null && !string.IsNullOrEmpty(sTextureLocation))
                    texture = contentManager.Load<Texture2D>(sTextureLocation);
            }
        }
        private string m_sLine;                                     // Entire - Input Line
        private bool m_bIsEnabled;
        private List<LineContainer> m_lHistory;
        private List<LineContainer> m_lHistoryOutput;               // Current state of the output being displayed. (Changed when window resizes and when printing to dev. console occurs.)
        // Contains no '\n', instead an entry contains a line number. No entry overflows onto another line.
        private List<string> m_lPreviousCommands;
        private KeyboardState m_previousState, m_currentState;
        private MouseState m_previousMouseState, m_currentMouseState;
        private ContentManager m_contentManager;
        private FocusState m_FocusState;

        private int m_MaxLineCount;
        private int m_iInputIndex;
        private int m_iLineDifferential;
        private Vector2 m_position;

        // Resize vars
        private Point m_MinResize;
        private bool m_bIsResizeEnabled;
        private Dictionary<string, DisplayFields> m_dFields;

        // Movement
        private bool m_bIsMoveEnabled;

        // Padding Vars
        float m_padding_top;
        float m_padding_bottom;
        float m_padding_right;
        float m_padding_left;
        float m_padding_history_input;
        int m_iPadding_input;

        // Drawing boxes to HUD
        BasicEffect effect;
        VertexDeclaration vertexDeclaration;

        public static ConVar enable_debugBoxes = new ConVar("enable_debugBoxes", false, ConsoleFlags.NONE, "Enables/Disables debug boxes for the console.");

        // For Non-Unchained engine.
        private GraphicsDevice graphics;    // graphics device
        private SpriteBatch sprite;         // sprite bacth
        private SpriteFont fonts;           // sprite font     

        private static readonly DeveloperConsole singleton = new DeveloperConsole();

        public static DeveloperConsole Singleton
        {
            get
            {
                return singleton;
            }
        }

        private DeveloperConsole()
        {
            Initialize();
        }

        private void Initialize()
        {
            m_previousState = m_currentState = new KeyboardState();
            m_previousMouseState = m_currentMouseState = new MouseState();
            m_lHistory = new List<LineContainer>();
            m_lHistoryOutput = new List<LineContainer>();
            m_lPreviousCommands = new List<string>();
            m_sLine = "";
            m_bIsEnabled = false;
            m_MaxLineCount = 0;
            m_iInputIndex = 0;
            m_iLineDifferential = 0;
            m_FocusState = FocusState.INPUT;
            m_bIsResizeEnabled = false;
            m_dFields = new Dictionary<string, DisplayFields>();
            m_bIsMoveEnabled = false;

            /* Harcoded Values */
            m_dFields.Add("Base_Texture", new DisplayFields(0, 0, 1024, 700, "HUD/Base_Texture"));
            m_dFields.Add("History_Field", new DisplayFields(35, 50, 950, 580, ""));
            m_dFields.Add("Input_Field", new DisplayFields(35, 659, 950, 25, ""));
            m_dFields.Add("Resize_Button", new DisplayFields(1024, 768, 20, 20, "HUD/Resize_Button"));
            
            m_position = new Vector2();
            m_MinResize = new Point(350, 200);
            m_padding_top = 25.0f;
            m_padding_bottom = 25.0f;
            m_padding_right = 25.0f;
            m_padding_left = 25.0f;
            m_padding_history_input = 30.0f;
            m_iPadding_input = 3;

            /* Test code goes below here */
            int i = Globals.cltest.GetInt();
            ApplyPadding();
            SetupDisplayFields();
        }
        public void LoadContent(ContentManager contentManager)
        {
            m_contentManager = contentManager;
            if (m_contentManager != null)
            {
                fonts = m_contentManager.Load<SpriteFont>("fonts/ArialS");

                // Load Textures
                if (m_dFields.Count > 0)
                {
                    Dictionary<string, DisplayFields>.KeyCollection keyCollection = m_dFields.Keys;
                    string[] keys = new string[keyCollection.Count];
                    keyCollection.CopyTo(keys, 0);
                    for (int i = 0; i < keyCollection.Count; i++)
                    {
                        DisplayFields displayField = m_dFields[keys[i]];
                        displayField.LoadTexture(contentManager);
                        m_dFields[keys[i]] = displayField;
                    }
                }
            }
        }
        public void UnloadContent()
        {

        }

        public void Draw2D(GraphicsDevice gd)
        {
            if (!m_bIsEnabled)
                return;     // Developer Console is disabled. Do nothing.
            if (sprite == null || graphics == null)
            {
                graphics = gd;
                sprite = new SpriteBatch(gd);

                // Fx, vertex, graphics device stuff.
                effect = new BasicEffect(graphics, null);
                vertexDeclaration = new VertexDeclaration(graphics, VertexPositionColor.VertexElements);
                graphics.VertexDeclaration = vertexDeclaration;
            }
            Texture2D texture = new Texture2D(gd, 1, 1);
            texture.SetData(new[] { Color.White });
            Rectangle box = LocalToWorldRectangle(m_dFields["Base_Texture"].value);
            // ** Temporary Draw Pattern **
            // Draw Background Texture
            Rectangle source = new Rectangle(0, 0, box.Width,
                box.Height);
            sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
            graphics.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            graphics.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            sprite.Draw(m_dFields["Base_Texture"].texture, new Vector2(box.X, box.Y), box, Color.White, 0,
    new Vector2(0, 0), 1.0f, SpriteEffects.None, 1.0f);
            sprite.End();

            // Draw Text
            box = LocalToWorldRectangle(m_dFields["Input_Field"].value);
            sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
            sprite.Draw(texture, box, new Color(50, 50, 50));
            sprite.End(); 
            box.X += m_iPadding_input;
            sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
            sprite.DrawString(fonts, GetInputLine(), GetRectanglePosition(box), Color.White);
            sprite.End();

            // Final Draw Pattern (TODO: Niels - Once I get the 2D art for this.)
            // Draw Background
            // Draw Scrollbar
            // Draw Console
            // Draw Console Input Line
            // Draw Submit
            // Draw Console Text
            box = LocalToWorldRectangle(m_dFields["History_Field"].value);
            sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
            sprite.Draw(texture, box,new Color(50, 50, 50));
            sprite.End(); 
            int iStartPosition = GetConsoleStart();
            int iEndPosition = GetConsoleEnd();
            if (iStartPosition >= 0)
            {
                Vector2 vWorldPosition = GetRectanglePosition(LocalToWorldRectangle(m_dFields["History_Field"].value));
                vWorldPosition.X += m_iPadding_input;
                Vector2 vConsolePrintPosition = new Vector2(vWorldPosition.X, vWorldPosition.Y);
                int iLine = -1;
                for (int i = iStartPosition; i <= iEndPosition; i++)
                {
                    if (iLine == -1)
                        iLine = m_lHistoryOutput[i].iLine;
                    if (iLine != m_lHistoryOutput[i].iLine)
                    {
                        vConsolePrintPosition.X = vWorldPosition.X;
                        vConsolePrintPosition.Y += fonts.MeasureString("A").Y;
                        iLine = m_lHistoryOutput[i].iLine;
                    }
                    sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
                    sprite.DrawString(fonts, m_lHistoryOutput[i].sValue, vConsolePrintPosition, m_lHistoryOutput[i].color);
                    sprite.End();
                    vConsolePrintPosition.X += fonts.MeasureString(m_lHistoryOutput[i].sValue).X;
                }
            }
            // Draw Input Text

            // Draw Resize Button
            sprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
            sprite.Draw(m_dFields["Resize_Button"].texture, LocalToWorldRectangle(m_dFields["Resize_Button"].value), Color.White);
            sprite.End(); 

            // Debug Draw Boxes
            if (enable_debugBoxes.GetBool())
            {
                DrawBoxToHUD(LocalToWorldRectangle(m_dFields["Input_Field"].value), Color.Red);
                DrawBoxToHUD(LocalToWorldRectangle(m_dFields["History_Field"].value), Color.Red);
                DrawBoxToHUD(LocalToWorldRectangle(m_dFields["Resize_Button"].value), Color.Red);
            }
            
            // Draw borders
            DrawBorder("History_Field", Color.White, 2);
            DrawBorder("Input_Field", Color.White, 2);

            // Draw over laying border
            DrawBorder("Base_Texture", Color.Black, 1);
        }

        /// <summary>
        /// Process keyboard input
        /// </summary>
        public bool ProcessInput(KeyboardState currentKeyboard, MouseState currentMouse)
        {
            // Update keyboard state.
            m_previousState = m_currentState;
            m_currentState = currentKeyboard;

            // Update mouse state.
            m_previousMouseState = m_currentMouseState;
            m_currentMouseState = currentMouse;

            // Toggle the console.
            if (m_previousState.IsKeyUp(Keys.OemTilde) && m_currentState.IsKeyDown(Keys.OemTilde))
                Toggle();

            if (!m_bIsEnabled)
                return false;     // Developer Console is disabled. Do nothing.

            // Process Mouse input
            HandleMouseInput(currentMouse);

            if (m_previousState == m_currentState)
                return true;    // Nothing to process

            // Process Keyboard input
            if (m_FocusState == FocusState.INPUT)
                HandleConsoleInput();
            else
                HandleConsoleHistoryInput();

            return true;
        }

        /// <summary>
        /// Enables/disables the developer console.
        /// </summary>
        public static ConVar cl_debug = new ConVar("cl_debug", 0, ConsoleFlags.NONE, "Is debug printing on or off?");
        public void Toggle()
        {
            m_bIsEnabled = !m_bIsEnabled;
            if (m_bIsEnabled)
            {
                m_sLine = "";
                m_FocusState = FocusState.INPUT;
                SetupDisplayFields();
            }
            if (cl_debug.GetBool())
            {
                if (m_bIsEnabled)
                    Console.WriteLine("Console is on.");
                else
                    Console.WriteLine("Console is off.");
            }
        }

        public void DevMsg(string value, Color color)
        {
            m_iLineDifferential = 0;
            LineContainer line = new LineContainer(value, color);
            m_lHistory.Add(line);
            UpdateHistoryOutput(line);

        }
        public void DevMsg(string value)
        {
            DevMsg(value, Color.White);
        }
        public bool IsEnabled()
        {
            return m_bIsEnabled;
        }
        private string GetInputLine()
        {
            int iEarliestIndex = -1;
            // Get max length
            int iLength = m_dFields["Input_Field"].value.Width - m_iPadding_input;

            // Determine the amount of letters in the input line that can be displayed.
            for (int i = m_sLine.Length - 1; i >= 0; i--)
            {
                iEarliestIndex = i;
                if ((int)fonts.MeasureString(m_sLine.Substring(i)).X > iLength)
                {
                    iEarliestIndex++;
                    break;
                }

            }
            // We now have the earliest index of the string.
            if (iEarliestIndex >= 0)
                return m_sLine.Substring(iEarliestIndex);
            else
                return "";
        }

        private void HandleMouseInput(MouseState currentMouse)
        {
            // Handle OnPressed
            if (m_previousMouseState.LeftButton == ButtonState.Released &&
                m_currentMouseState.LeftButton == ButtonState.Pressed)
            {
                bool bInteractedWith = false;

                // Check if we hit the history box.
                if (IsPointInBox(LocalToWorldRectangle(m_dFields["History_Field"].value),
                                 new Point(m_currentMouseState.X, m_currentMouseState.Y)))
                {
                    m_FocusState = FocusState.HISTORY;
                    bInteractedWith = true;
                }

                // Check if we hit the input box.
                if (IsPointInBox(LocalToWorldRectangle(m_dFields["Input_Field"].value),
                                  new Point(m_currentMouseState.X, m_currentMouseState.Y)))
                {
                    m_FocusState = FocusState.INPUT;
                    bInteractedWith = true;
                }
                // Check for enabling resize
                if (IsPointInBox(LocalToWorldRectangle(m_dFields["Resize_Button"].value),
                         new Point(m_currentMouseState.X, m_currentMouseState.Y)))
                {
                    m_bIsResizeEnabled = true;
                    bInteractedWith = true;
                }
                // Check for enabling movement
                if (!bInteractedWith && IsPointInBox(LocalToWorldRectangle(m_dFields["Base_Texture"].value),
                                        new Point(m_currentMouseState.X, m_currentMouseState.Y)))
                    m_bIsMoveEnabled = true;

            }
            // Handle OnHeld
            else if (m_previousMouseState.LeftButton == ButtonState.Pressed &&
                     m_currentMouseState.LeftButton == ButtonState.Pressed)
            {
                // Check if we hit the resize button.
                if (m_bIsResizeEnabled)
                {
                    DoResize();
                }
                else if (m_bIsMoveEnabled)
                {
                    DoMove();
                }
            }
            // Handle OnRelease
            else if (m_previousMouseState.LeftButton == ButtonState.Pressed &&
                     m_currentMouseState.LeftButton == ButtonState.Released)
            {
                m_bIsResizeEnabled = m_bIsMoveEnabled = false;
            }


            // Check if we Hit the scroll-bar.
            // A. Top button
            // B. Bottom button
            // C. Slider
            // D. Scroll-bar

            // Check if we hit the close button.

        }

        private void DoResize()
        {
            // The differential of mouse movement.
            float xDiff = m_currentMouseState.X - m_previousMouseState.X,
                  yDiff = m_currentMouseState.Y - m_previousMouseState.Y;

            // Resize Button
            DisplayFields displayField = m_dFields["Resize_Button"];
            displayField.value.X = (int)MathHelper.Clamp(displayField.value.X + xDiff, m_MinResize.X, graphics.Viewport.Width - displayField.value.Width);
            displayField.value.Y = (int)MathHelper.Clamp(displayField.value.Y + yDiff, m_MinResize.Y, graphics.Viewport.Height - displayField.value.Height);
            m_dFields["Resize_Button"] = displayField;

            // Background
            displayField = m_dFields["Base_Texture"];
            displayField.value.Width = (int)MathHelper.Clamp(displayField.value.Width + xDiff, m_MinResize.X + m_dFields["Resize_Button"].value.Width, graphics.Viewport.Width);
            displayField.value.Height = (int)MathHelper.Clamp(displayField.value.Height + yDiff, m_MinResize.Y + m_dFields["Resize_Button"].value.Height, graphics.Viewport.Height);
            m_dFields["Base_Texture"] = displayField;

            // Update fields as a whole
            UpdateDisplayFields();

            // Update the History
            // TODO: Only update if the line length to accomodate one letter changed. More efficient, one variable.
            m_lHistoryOutput.Clear();   // Empty the current history out.
            foreach (LineContainer lineContainer in m_lHistory)
            {
                UpdateHistoryOutput(lineContainer);
            }

            // Re-position Mouse.
        }

        private void UpdateDisplayFields()
        {
            DisplayFields displayField = m_dFields["Base_Texture"];
            float fPositionRight = displayField.value.Width - (m_padding_right + m_padding_left);
            // History
            displayField = m_dFields["History_Field"];
            displayField.value.Width = (int)fPositionRight;
            displayField.value.Height = (int)(m_dFields["Base_Texture"].value.Height -
                                        (m_padding_top + m_padding_bottom + m_dFields["Input_Field"].value.Height + m_padding_history_input));
            m_dFields["History_Field"] = displayField;

            // Input
            displayField = m_dFields["Input_Field"];
            displayField.value.Width = (int)fPositionRight;
            displayField.value.Y = (int)(m_dFields["History_Field"].value.Y + m_dFields["History_Field"].value.Height + m_padding_history_input);
            m_dFields["Input_Field"] = displayField;
        }

        private void SetupDisplayFields()
        {
            // Entire Console
            m_position = new Vector2();

            DisplayFields displayField = m_dFields["Base_Texture"];
            displayField.value = displayField.defaultValue;
            m_dFields["Base_Texture"] = displayField;

            // History
            displayField = m_dFields["History_Field"];
            displayField.value = displayField.defaultValue;
            m_dFields["History_Field"] = displayField;

            // Input
            displayField = m_dFields["Input_Field"];
            displayField.value = displayField.defaultValue;
            m_dFields["Input_Field"] = displayField;

            // Resize Button
            displayField = m_dFields["Resize_Button"];
            displayField.value = displayField.defaultValue;
            m_dFields["Resize_Button"] = displayField;
        }

        // Should only be called ONCE (No more than once unless you are doing something wrong or resizing the game window).
        private void ApplyPadding()
        {
            // History
            DisplayFields displayField = m_dFields["History_Field"];
            displayField.defaultValue.Y = (int)(m_padding_top + m_position.X);
            displayField.defaultValue.X = (int)(m_padding_left + m_position.Y);
            displayField.defaultValue.Width = (int)(m_dFields["Base_Texture"].value.Width - (m_padding_left + m_padding_right));
            displayField.defaultValue.Height = (int)(m_dFields["Base_Texture"].value.Height -
                                               (m_padding_top + m_padding_bottom + m_dFields["Input_Field"].defaultValue.Height + m_padding_history_input));
            displayField.value = displayField.defaultValue;
            m_dFields["History_Field"] = displayField;

            // Input
            displayField = m_dFields["Input_Field"];
            displayField.defaultValue.Y = (int)(m_dFields["History_Field"].defaultValue.Y + m_dFields["History_Field"].defaultValue.Height + m_padding_history_input);
            displayField.defaultValue.X = m_dFields["History_Field"].defaultValue.X;
            displayField.defaultValue.Width = m_dFields["History_Field"].defaultValue.Width;
            displayField.value = displayField.defaultValue;
            m_dFields["Input_Field"] = displayField;

            // Resize Button
            displayField = m_dFields["Resize_Button"];
            displayField.defaultValue.Y = m_dFields["Base_Texture"].value.Height - displayField.defaultValue.Height;
            displayField.defaultValue.X = m_dFields["Base_Texture"].value.Width - displayField.defaultValue.Width;
            displayField.value = displayField.defaultValue;
            m_dFields["Resize_Button"] = displayField;
        }

        private void DoMove()
        {
            // The differential of mouse movement.
            float xDiff = m_currentMouseState.X - m_previousMouseState.X,
                  yDiff = m_currentMouseState.Y - m_previousMouseState.Y;

            m_position.X += xDiff;
            m_position.Y += yDiff;
        }

        private void HandleConsoleInput()
        {
            List<Keys> lSpecialA, lSymbols, lSpecialB, lSpecialC;
            lSpecialA = new List<Keys>();
            lSymbols = new List<Keys>();
            lSpecialB = new List<Keys>();
            lSpecialC = new List<Keys>();
            bool bIsEnterPressed = false;
            bool bIsTabPressed = false;

            Keys[] currentKeys = m_currentState.GetPressedKeys();
            Keys[] previousKeys = m_previousState.GetPressedKeys();
            // Pass 1:  Sort keys. Special, letters/numbers, Special2
            //          Special - shift, caps lock, ctrl, alt
            //          Letters/Numbers - what you expect
            //          Special 2 - space, backspace , delete
            //          Arrows + pgup/down(HACK)
            foreach (Keys currentKey in currentKeys)
            {
                bool bKeyIsJustPressed = true;
                foreach (Keys previousKey in previousKeys)
                {
                    if (currentKey == previousKey)
                    {
                        bKeyIsJustPressed = false;
                        break;
                    }
                }

                if (currentKey == Keys.RightShift || currentKey == Keys.LeftShift ||
                    currentKey == Keys.RightControl || currentKey == Keys.LeftControl ||
                    currentKey == Keys.RightAlt || currentKey == Keys.LeftAlt ||
                    currentKey == Keys.CapsLock)
                    lSpecialA.Add(currentKey);

                if (bKeyIsJustPressed)
                {
                    if ((currentKey >= Keys.A && currentKey <= Keys.Z) ||
                        (currentKey >= Keys.D0 && currentKey <= Keys.D9) ||
                        currentKey == Keys.OemMinus ||
                        currentKey == Keys.OemQuotes)
                    {
                        lSymbols.Add(currentKey);
                        m_iInputIndex = m_lPreviousCommands.Count;// Reset index
                    }
                    if (currentKey == Keys.Space || currentKey == Keys.Delete ||
                        currentKey == Keys.Back)
                    {
                        lSpecialB.Add(currentKey);
                        m_iInputIndex = m_lPreviousCommands.Count;// Reset index
                    }
                    if (currentKey == Keys.Up || currentKey == Keys.Down ||
                        currentKey == Keys.PageUp || currentKey == Keys.PageDown)
                        lSpecialC.Add(currentKey);
                    if (currentKey == Keys.Enter)
                    {
                        bIsEnterPressed = true;
                        m_iInputIndex = m_lPreviousCommands.Count;// Reset index
                    }
                    if (currentKey == Keys.Tab)
                    {
                        bIsTabPressed = true;
                        m_iInputIndex = m_lPreviousCommands.Count;// Reset index
                    }
                }
            }

            // Pass 2: Process keys in order
            bool bIsCapitalized = false;
            string sLine = "";
            foreach (Keys key in lSpecialA)
            {
                if (key == Keys.RightShift || key == Keys.LeftShift)
                    bIsCapitalized = true;
            }
            foreach (Keys key in lSymbols)
            {
                if (key >= Keys.A && key <= Keys.Z && !bIsCapitalized)  // Letters
                {
                    // Non-capitalized letters
                    int iDiff = Convert.ToInt32('a') - Convert.ToInt32('A');
                    int iChar = Convert.ToInt32(Convert.ToChar(key.ToString()));
                    sLine = string.Concat(sLine, Convert.ToChar(iChar + iDiff));
                }
                else if (key >= Keys.D0 && key <= Keys.D9)
                {
                    // Numbers
                    int iChar = Convert.ToInt32('0');
                    int iDiff = key - Keys.D0;
                    sLine = string.Concat(sLine, Convert.ToChar(iChar + iDiff));
                }
                else if (key == Keys.OemMinus)
                {
                    if (bIsCapitalized)
                        sLine = string.Concat(sLine, "_");
                }
                else if (key == Keys.OemQuotes)
                {
                    if (bIsCapitalized)
                        sLine = string.Concat(sLine, "\"");
                }
                else
                {
                    // Capitalized letters, Symbols
                    sLine = string.Concat(sLine, key.ToString());
                }
            }
            foreach (Keys key in lSpecialB)
            {
                if (key == Keys.Space)
                    sLine = string.Concat(sLine, " ");
                if (key == Keys.Back)
                {
                    if (!string.IsNullOrEmpty(sLine))
                        sLine = sLine.Remove(sLine.Length - 1);
                    else if (!string.IsNullOrEmpty(m_sLine))
                        m_sLine = m_sLine.Remove(m_sLine.Length - 1);
                }
            }

            m_sLine = string.Concat(m_sLine, sLine);

            foreach (Keys key in lSpecialC)
            {
                string temp = "";
                if (key == Keys.Up || key == Keys.Down)
                    temp = GetAPreviousCommand(key == Keys.Up);
                if (key == Keys.PageUp || key == Keys.PageDown)
                    UpdateHistoryLineDiff(key == Keys.PageUp);
                if (!string.IsNullOrEmpty(temp))
                    m_sLine = temp;
            }
            // Auto-complete?
            if (bIsTabPressed)
            {
                int iNumArgs = -1;
                string[] words = m_sLine.Split(' ');
                foreach (string word in words)
                {
                    iNumArgs++;
                }
                if (iNumArgs == 0)
                {
                    AutoComplete();
                }
            }
            // Process the line
            if (bIsEnterPressed)
            {
                // print the message
                DevMsg("] " + m_sLine + "\n", Color.White);
                if (string.IsNullOrEmpty(m_sLine)) // empty string
                {
                    m_sLine = "";
                    m_iInputIndex = m_lPreviousCommands.Count;// Reset index
                    return;// do nothing;
                }

                int iNumArgs = -1;
                string[] words = m_sLine.Split(' ');
                string consoleOutput = "";
                // New stuff incorporating quotes into input
                int iCounter;
                List <int> quotestarts = new List<int>();
                List<string> arguments = new List<string>();
                foreach (string word in words)
                {
                    iNumArgs++;
                }
                // Lets check for "quoted" strings
                iCounter = 0;
                iCounter = m_sLine.IndexOf('"', iCounter);
                while (iCounter >= 0 && iCounter < m_sLine.Length)
                {
                    quotestarts.Add(iCounter);
                    iCounter++;
                    int previous = iCounter;
                    iCounter = m_sLine.IndexOf('"', iCounter);
                    if (previous == iCounter)
                        iCounter = -1;
                }
                if (quotestarts.Count >= 2 && quotestarts.Count%2 == 0)
                {
                    for (int i = 0; i < quotestarts.Count; i += 2)
                        arguments.Add(m_sLine.Substring(quotestarts[i], quotestarts[i + 1] - quotestarts[i]));
                }

                
                if (iNumArgs == 0)  // command
                {
                    if (string.Compare(words[0], "clear") == 0)
                        Clear();
                    else if (ConCommandMap.Singleton.Invoke(words[0], null))
                    { } // Do nothing;
                    else
                        consoleOutput = "Unknown command \"" + m_sLine + "\".\n";
                }
                else if (iNumArgs == 1)  // key value
                {
                    if (string.Compare(words[0], "help") == 0)
                        Help(words[1]);
                    else if (string.Compare(words[0], "find") == 0)
                        Find(words[1]);
                    else if (ConVarMap.Singleton.SetConVar(words[0], words[1]))
                    { } // Do nothing; 
                    else if (arguments.Count > 0)
                    {
                        ConVarMap.Singleton.SetConVar(words[0], arguments[0].Substring(1,arguments[0].Length-2));
                        object [] objects = { arguments[0].Substring(1, arguments[0].Length - 1)};
                        ConCommandMap.Singleton.Invoke(words[0], objects);
                    }
                    else
                        consoleOutput = "Unknown command \"" + m_sLine + "\".\n";
                }
                else if (!string.IsNullOrEmpty(words[0]) && arguments.Count > 0)
                { 
                    // For the moment only handles 1 pair of quotes
                    string line = arguments[0].Substring(1, arguments[0].Length - 1);
                    string[] args = line.Split(' ');

                    object[] objects = new object[args.Length];
                    for (int i = 0; i < args.Length; i++)
                    {
                        objects[i] = args[i];
                    }
                    ConCommandMap.Singleton.Invoke(words[0], objects);
                }
                else
                    consoleOutput = "Unknown command \"" + m_sLine + "\".\n";

                DevMsg(consoleOutput, Color.White);
                if (!string.IsNullOrEmpty(m_sLine))
                    m_lPreviousCommands.Add(m_sLine);
                m_sLine = "";
                m_iInputIndex = m_lPreviousCommands.Count;// Reset index
            }
        }

        private void HandleConsoleHistoryInput()
        {
        }

        /**  HELPER FUNCTIONS  **/
        private bool IsButtonJustPressed(Buttons button)
        {
            bool bIsButtonJustPressed = false;

            // Handle Keyboard

            // Handle Mouse

            return bIsButtonJustPressed;

        }
        private bool IsButtonHeld(Buttons button)
        {
            bool bIsButtonHeld = false;

            return bIsButtonHeld;
        }

        private bool IsPointInBox(Rectangle box, Point point)
        {
            if ((box.Left <= point.X && point.X <= box.Right) &&
                (box.Top <= point.Y && point.Y <= box.Bottom))
                return true;
            return false;
        }

        private Rectangle LocalToWorldRectangle(Rectangle rectangle)
        {
            if (rectangle == null || m_position == null)
                return rectangle;   // Nothing to process.
            return new Rectangle(rectangle.X + (int)m_position.X, rectangle.Y + (int)m_position.Y,
                                 rectangle.Width, rectangle.Height);
        }

        private Vector2 GetRectanglePosition(Rectangle rectangle)
        {
            if (rectangle == null)
                return Vector2.Zero;   // Nothing to process.
            return new Vector2(rectangle.X, rectangle.Y);
        }
        private void DrawBoxToHUD(Rectangle rectangle, Color color)
        {
            // Declare the rectangle points
            // Diagram of points
            /*
             *  1-------2
             *  |       |
             *  |       |
             *  4-------3
             */
            VertexPositionColor[] vertices = new VertexPositionColor[8];
            Vector3 vOne = new Vector3((float)2 * rectangle.X / graphics.Viewport.Width - 1,
                                         1 - (float)2 * rectangle.Y / graphics.Viewport.Height, 0),
                    vTwo = new Vector3((float)2 * (rectangle.X + rectangle.Width) / graphics.Viewport.Width - 1,
                                         1 - (float)2 * rectangle.Y / graphics.Viewport.Height, 0),
                    vThree = new Vector3((float)2 * (rectangle.X + rectangle.Width) / graphics.Viewport.Width - 1,
                                         1 - (float)2 * (rectangle.Y + rectangle.Height) / graphics.Viewport.Height, 0),
                    vFour = new Vector3((float)2 * rectangle.X / graphics.Viewport.Width - 1,
                                         1 - (float)2 * (rectangle.Y + rectangle.Height) / graphics.Viewport.Height, 0);

            // Top (1-2)
            vertices[0] = new VertexPositionColor(vOne, color);
            vertices[1] = new VertexPositionColor(vTwo, color);
            // Left (2-3)
            vertices[2] = new VertexPositionColor(vTwo, color);
            vertices[3] = new VertexPositionColor(vThree, color);
            // Bottom (3-4)
            vertices[4] = new VertexPositionColor(vThree, color);
            vertices[5] = new VertexPositionColor(vFour, color);
            // Right (4-1)
            vertices[6] = new VertexPositionColor(vFour, color);
            vertices[7] = new VertexPositionColor(vOne, color);

            // Draw Method
            effect.VertexColorEnabled = true;
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                graphics.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 4);
                pass.End();
            }
            effect.End();
        }

        // ToDo: Make size work
        private void DrawBorder(string key, Color color, int size)
        {
            Rectangle box = LocalToWorldRectangle(m_dFields[key].value);
            if (box != null)
            {
                // Borders (Top, Bottom, Left, Right)
                //fontManager.DrawTexture(m_border, new Rectangle(box.X, box.Y - 5 , box.Width, 5), Color.White, SpriteBlendMode.None);
                //fontManager.DrawTexture(m_border, new Rectangle(box.X, box.Y + box.Height, box.Width, 5), Color.White, SpriteBlendMode.None);
                //fontManager.DrawTexture(m_border, new Rectangle(box.X - 5, box.Y, 5, box.Height), Color.White, SpriteBlendMode.None);
                //fontManager.DrawTexture(m_border, new Rectangle(box.X + box.Width, box.Y, 5, box.Height), Color.White, SpriteBlendMode.None);
                // Corners (Top left, Bottom left, Top right, Bottom right)
                //fontManager.DrawTexture(m_corner, new Rectangle(box.X - 5, box.Y - 5, 5, 5), MathHelper.Pi, Color.White, SpriteBlendMode.Additive);
                //fontManager.DrawTexture(m_corner, new Rectangle(box.X - 5, box.Y + box.Height, 5, 5), MathHelper.PiOver2, Color.White, SpriteBlendMode.Additive);
                //fontManager.DrawTexture(m_corner, new Rectangle(box.X + box.Width, box.Y - 5, 5, 5), -MathHelper.PiOver2, Color.White, SpriteBlendMode.Additive);
                //fontManager.DrawTexture(m_corner, new Rectangle(box.X + box.Width, box.Y + box.Height, 5, 5), Color.White, SpriteBlendMode.Additive);.
                box.Width += 1;
                box.Height += 1;
                box.X -= 1;
                box.Y -= 1;
                DrawBoxToHUD(box, color);
                if (size > 1)
                {
                    box.Width += 2;
                    box.Height += 2;
                    box.X -= 1;
                    box.Y -= 1;
                    DrawBoxToHUD(box, color);
                }
                //box.Width += 2;
                //box.Height += 2;
                //box.X -= 1;
                //box.Y -= 1;
                //DrawBoxToHUD(box, Color.White);
            }
        }

        private void Help(string key)
        {
            ConVar possibleConvar = ConVarMap.Singleton.GetConVar(key);
            ConCommand possibleConCommand = ConCommandMap.Singleton.GetConCommand(key);
            if (possibleConvar == null && possibleConCommand == null)
                DevMsg("Usage:  help <cvarname>\n");
            else
            {
                string sLineA, sLineB;
                sLineA = sLineB = "";
                if (possibleConvar != null)
                {
                    sLineA = " \"" + possibleConvar.GetKey() + "\" = \"" + possibleConvar.GetString() + "\" ( def. \"" + possibleConvar.GetDefaultValue() + "\" )\n";
                    sLineB = " - " + possibleConvar.GetDescription() + "\n";
                    DevMsg(sLineA, Color.Red);
                    DevMsg(sLineB);
                }
                if (possibleConCommand != null)
                {
                    sLineA = " \"" + possibleConCommand.GetKey() + "\" = cmd.\n";
                    sLineB = " - " + possibleConCommand.GetDescription() + "\n";
                    DevMsg(sLineA, Color.Red);
                    DevMsg(sLineB);
                }
            }
        }

        private void Find(string key)
        {
            // Get all possible keys
            List<string> theKeys = ConVarMap.Singleton.Find(key);
            theKeys.AddRange(ConCommandMap.Singleton.Find(key));
            theKeys.Sort();

            foreach (string sKey in theKeys)
            {
                ConVar possibleConVar = ConVarMap.Singleton.GetConVar(sKey);
                ConCommand possibleConCommand = ConCommandMap.Singleton.GetConCommand(sKey);
                string sLineA, sLineB;
                sLineA = sLineB = "";
                if (possibleConVar != null)
                {
                    sLineA = "\"" + possibleConVar.GetKey() + "\"\n";
                    sLineB = " - " + possibleConVar.GetDescription() + "\"\n";
                    DevMsg(sLineA, Color.Red);
                    DevMsg(sLineB);
                }
                if (possibleConCommand != null)
                {
                    sLineA = "\"" + possibleConCommand.GetKey() + "\"\n";
                    sLineB = " - " + possibleConCommand.GetDescription() + "\"\n";
                    DevMsg(sLineA, Color.Red);
                    DevMsg(sLineB);
                }
            }
            if (theKeys.Count == 0)
                DevMsg("Usage:  find <string>\n");
        }
        private void Clear()
        {
            m_lHistory.Clear();
            m_lHistoryOutput.Clear();
            m_MaxLineCount = 0;
        }
        // ToDo: Make it better;
        private void AutoComplete()
        {
            // Default case(s)
            if (string.IsNullOrEmpty(m_sLine))
                return;
            ConVar possibleConVar = ConVarMap.Singleton.GetConVar(m_sLine);
            if (possibleConVar != null)
            {
                m_sLine += " " + possibleConVar.GetString();
                return;
            }
            ConCommand possibleConCommand = ConCommandMap.Singleton.GetConCommand(m_sLine);
            if (possibleConCommand != null)
                return;

            // Do the autocomplete algorithm
            List<string> keys = ConCommandMap.Singleton.Keys();
            keys.AddRange(ConVarMap.Singleton.Keys());
            List<string> values = new List<string>();
            foreach (string key in keys)
            {
                if (m_sLine.Length < key.Length)       // key.length < pairKey.length
                {
                    if (string.Compare(m_sLine, key.Substring(0, m_sLine.Length)) == 0)
                        values.Add(key);
                }
            }

            // No matching values?
            if (values.Count <= 0)
                return;

            // Only 1 matching value
            if (values.Count == 1)
            {
                possibleConVar = ConVarMap.Singleton.GetConVar(values[0]);
                m_sLine = values[0];
                if (possibleConVar != null)
                    m_sLine += " " + possibleConVar.GetString();
                return;
            }

            // We have some keys to compare.
            List<int> lengths = new List<int>();
            foreach (string foundKey in values)
            {
                foreach (string foundKeyB in values)
                {
                    if (string.Compare(foundKey, foundKeyB) != 0)
                    {
                        int iSmallestLength = foundKey.Length > foundKeyB.Length ? foundKeyB.Length : foundKey.Length;
                        iSmallestLength -= m_sLine.Length;
                        if (string.Compare(foundKey.Substring(m_sLine.Length, iSmallestLength), foundKeyB.Substring(m_sLine.Length, iSmallestLength)) == 0)
                            lengths.Add(iSmallestLength + m_sLine.Length);
                        else
                        {
                            string one, two;
                            one = foundKey.Substring(m_sLine.Length, iSmallestLength);
                            two = foundKeyB.Substring(m_sLine.Length, iSmallestLength);
                            for (int i = 0; i < iSmallestLength; i++)
                            {
                                if (one[i] != two[i])
                                {
                                    if (i == 0)
                                        break;
                                    lengths.Add(i + m_sLine.Length);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            // Comparison does go beyond the 'key'
            if (lengths.Count <= 0)
                return;

            // We now have the lengths
            int iOptimalLength = lengths[0];
            for (int i = 1; i < lengths.Count; i++)
            {
                if (iOptimalLength > lengths[i])
                    iOptimalLength = lengths[i];
            }

            m_sLine = values[0].Substring(0, iOptimalLength);
        }

        private void UpdateHistoryOutput(LineContainer theLineContainer)
        {
            if (!theLineContainer.Equals(null) && !string.IsNullOrEmpty(theLineContainer.sValue))
            {
                // Replace all '\n' characters with a '|' to signify a end of line while parsing the input.
                string line = theLineContainer.sValue.Replace('\n', '|');

                // Is there any characters on the current line?
                float iLineLength = 0;
                for (int i = m_lHistoryOutput.Count - 1; i >= 0; i--)
                {
                    if (m_lHistoryOutput[i].iLine == m_MaxLineCount)
                        iLineLength += fonts.MeasureString(m_lHistoryOutput[i].sValue).X;
                    else
                        break;
                }

                // Add new entry now that we have the current line
                List<LineContainer> lLineAdditions = new List<LineContainer>();
                Vector2 vLengthOfString;
                string lineTemp = "", lineReal = "";
                for (int i = 0; i < line.Length; i++)
                {
                    lineTemp = string.Concat(lineTemp, line[i]);
                    // Is string length valid?
                    vLengthOfString = fonts.MeasureString(lineTemp);
                    if (vLengthOfString.X + iLineLength > m_dFields["History_Field"].value.Width - m_iPadding_input || string.Compare(line[i].ToString(), "|") == 0)
                    {
                        LineContainer tempLine = new LineContainer(lineReal, theLineContainer.color);
                        tempLine.iLine = m_MaxLineCount;
                        lLineAdditions.Add(tempLine);
                        // do update
                        m_MaxLineCount++;
                        iLineLength = 0;
                        if (string.Compare(line[i].ToString(), "|") != 0)
                            lineReal = line[i].ToString();
                        else
                            lineReal = "";
                        lineTemp = line[i].ToString();
                    }
                    else
                        lineReal = string.Concat(lineReal, line[i]);
                }
                if (!string.IsNullOrEmpty(lineReal))
                {
                    LineContainer tempLineB = new LineContainer(lineReal, theLineContainer.color);
                    tempLineB.iLine = m_MaxLineCount;
                    lLineAdditions.Add(tempLineB);
                }

                // Add new lineContainers parsed from inputted lineContainer
                foreach (LineContainer templine in lLineAdditions)
                {
                    m_lHistoryOutput.Add(templine);
                }
            }
        }

        private int GetConsoleStart()
        {
            int iMaxNumberOfLines = GetNumberHistoryLines();
            if (m_lHistoryOutput.Count > 0 && iMaxNumberOfLines > 0)
            {
                int iValue = -1;
                int iLineNumber = (int)MathHelper.Clamp(m_lHistoryOutput[m_lHistoryOutput.Count - 1].iLine - iMaxNumberOfLines - m_iLineDifferential, 0, m_MaxLineCount);
                for (int i = 0; i < m_lHistoryOutput.Count; i++)
                {
                    if (iLineNumber <= m_lHistoryOutput[i].iLine)
                    {
                        iValue = i;
                        break;
                    }
                }
                return iValue;
            }
            else
                return -1;
        }

        private int GetConsoleEnd()
        {
            int iMaxNumberOfLines = GetNumberHistoryLines();
            if (m_lHistoryOutput.Count > 0 && iMaxNumberOfLines > 0)
            {
                int iValue = -1;
                int iLineNumber = (int)MathHelper.Clamp(m_lHistoryOutput[m_lHistoryOutput.Count - 1].iLine - m_iLineDifferential, 0, m_MaxLineCount);
                for (int i = 0; i < m_lHistoryOutput.Count; i++)
                {
                    if (iLineNumber <= m_lHistoryOutput[i].iLine)
                    {
                        iValue = i;
                        break;
                    }
                }
                return iValue;
            }
            else
                return -1;
        }

        private int GetNumberHistoryLines()
        {
            if (fonts != null)
            {
                int iNumLines = -1;
                float fHeightOfLines = 0;
                while ((int)fHeightOfLines < m_dFields["History_Field"].value.Height)
                {
                    fHeightOfLines += fonts.MeasureString("A").Y;
                    iNumLines++;
                }
                return iNumLines - 1;
            }
            else
                return -1;
        }

        private string GetAPreviousCommand(bool isPrevious)
        {
            string value = "";
            if (m_lPreviousCommands.Count <= 0)
                return null;    // Nothing to look at previous or next.

            if (isPrevious)     // Previous
            {
                m_iInputIndex--;
                if (m_iInputIndex < 0)
                    m_iInputIndex = m_lPreviousCommands.Count - 1;
                value = m_lPreviousCommands[m_iInputIndex];
            }
            else                // Next
            {
                m_iInputIndex++;
                if (m_iInputIndex > m_lPreviousCommands.Count - 1)
                    m_iInputIndex = 0;
                value = m_lPreviousCommands[m_iInputIndex];
            }
            return value;
        }

        private void UpdateHistoryLineDiff(bool bIsLess)
        {
            if (m_lHistoryOutput.Count <= 0)
                return;
            int iMax = (int)MathHelper.Clamp(m_lHistoryOutput[m_lHistoryOutput.Count - 1].iLine - GetNumberHistoryLines(), 0, m_MaxLineCount);
            if (bIsLess)
                m_iLineDifferential = (int)MathHelper.Clamp(m_iLineDifferential + 1, 0, iMax);
            else
                m_iLineDifferential = (int)MathHelper.Clamp(m_iLineDifferential - 1, 0, iMax);
        }
    }
}